﻿using System.Collections.Generic;

namespace Core
{
    /// <summary>
    /// 线性处理器
    /// 
    /// <para>支持接口:</para>
    /// <para>    IExecute，执行无状态一次性的动作</para>
    /// <para>    IStartUpdateFinish，执行有状态或者异步动作</para>
    /// <para>    IGotoProcess，根据需要判断的条件结果进行节点跳转</para>
    /// </summary>
    public class SequencePU : AbstractProcessingUnit, IAwake, IAwake<string>
    {
        private readonly List<Component> list = new();

        public void Awake() { }

        public void Awake(string name)
        {
            Name = name;
        }

        public int IndexOf(Core.Component process)
        {
            if (process == null)
            {
                return -1;
            }

            return list.IndexOf(process);
        }

        private int index;

        private K Create<K>()
            where K : Core.Component
        {
            K k = G.Factory.CreateInstance<K>();

            k.SetSG(S, G, Entity);

            if (k is IGotoProcess obj2)
            {
                obj2.Index = list.Count;
            }

            list.Add(k);

            return k;
        }

        public K Add<K>()
            where K : Core.Component, IInstruction
        {
            K k = Create<K>();
            G.Factory.Awake(k);
            return k;
        }

        public K Add<K, P1>(P1 p1)
            where K : Core.Component, IInstruction
        {
            K k = Create<K>();
            G.Factory.Awake(k, p1);
            return k;
        }

        public K Add<K, P1, P2>(P1 p1, P2 p2)
            where K : Core.Component, IInstruction
        {
            K k = Create<K>();
            G.Factory.Awake(k, p1, p2);
            return k;
        }

        public K Add<K, P1, P2, P3>(P1 p1, P2 p2, P3 p3)
            where K : Core.Component, IInstruction
        {
            K k = Create<K>();
            G.Factory.Awake(k, p1, p2, p3);
            return k;
        }

        public K Add<K, P1, P2, P3, P4>(P1 p1, P2 p2, P3 p3, P4 p4)
            where K : Core.Component, IInstruction
        {
            K k = Create<K>();
            G.Factory.Awake(k, p1, p2, p3, p4);
            return k;
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            Clear();
        }

        public void Clear()
        {
            foreach (var t in list)
            {
                t.Dispose();
            }
            list.Clear();
        }

        private Component lastObj;
        public override void Start()
        {
            index = 0;
            IsComplete = false;
            lastObj = null;

            Tick();
        }

        public override void Update()
        {
            if (IsComplete) return;

            Tick();
        }

        public override void Finish()
        {
            IsComplete = true;
        }


        private void Tick()
        {
            var element = list[index];
            switch (element)
            {
                case IStartUpdateFinish obj:
                    if (lastObj != obj)
                    {
                        obj.Start();
                        lastObj = obj as Component;
                    }
                    Tick(obj);
                    break;
                case IExecute obj:
                    Tick(obj);
                    break;
            }
        }

        private void Tick(IStartUpdateFinish curr)
        {
            curr.Update();

            if (!curr.IsComplete)
            {
                return;
            }

            curr.Finish();

            if (curr is IGotoProcess obj)
            {
                index = obj.JumpPoint;
            }
            else
            {
                index++;
            }


            lastObj = curr as Component;

            if (index >= list.Count)
            {
                IsComplete = true;
                index = 0;
                return;
            }

            Tick();
        }

        private void Tick(IExecute curr)
        {
            curr.Execute();
            index++;

            lastObj = curr as Component;

            if (index >= list.Count)
            {
                IsComplete = true;
                index = 0;
                return;
            }

            Tick();
        }

    }
}
